# -*- coding: utf-8 -*-
import hashlib
import re
import traceback
from operator import itemgetter

import regex
import requests
import scrapy

from ..utils.config_util import (
    proxy_dict,
    header_css_svg,
    bd_key
)


DigitLetterPATTERN = regex.compile(r"[^A-Za-z0-9]")


class BaseSpider(scrapy.Spider):
    """
    爬虫基类
    其他几个爬虫都继承此爬虫
    """
    def __init__(self):
        super().__init__()

        self.valid_response_status = 200
        # 获取百度API的url
        self.map_url = "http://api.map.baidu.com/geocoder?address=%s&output=json&key={bd_key}".format(bd_key=bd_key)

        # 正则表达式，用于各种数据的匹配
        self.pattern_text = regex.compile(r">(.*?)<", re.S)
        self.pattern_rubbish = regex.compile(r'电话：|条评论|\xa0')
        self.pattern_symbol = regex.compile(r">|<")
        self.pattern_svg_text_path = regex.compile(r'<textPath xlink:href="#(\d+)" textLength=".*?">(.*?)</textPath>',
                                                   re.S)
        self.pattern_tag_info = re.compile('<[a-z]+\s*class="([a-z0-9]+)"><.*?>', re.S)
        self.pattern_svg_defs = regex.compile(r'<path id="(\d+)" d="M0 (\d+) H600"/>', re.S)
        self.pattern_handler_svg = regex.compile(r"<text.*?y=\"(\d+)\">(.*?)</text>")
        self.pattern_is_existed_defs = regex.compile(r'<defs>', re.S)
        self.pattern_css_url = regex.compile(r"s3plus.meituan.net/.*?/svgtextcss/.*?\.css")
        self.pattern_svg_url = regex.compile(r"s3plus.meituan.net/.*?/svgtextcss/.*?\.svg")
        self.pattern_css_info = regex.compile('\.([a-z0-9]*?)\{background:-(\d+).0px -(\d+).0px;\}', re.S)
        self.pattern_svg_info = regex.compile('class\^=\"(.*?)\"\]\{width: *(\d+)px.*?(//.*?\.svg).*?\}', re.S)

    def parse(self, response):
        pass

    def is_valid_response(self, response):
        """
        判断响应对象是否可用
        """
        if response.status != self.valid_response_status:
            return False
        if "redirect_urls" not in response.request.meta:
            return True
        for raw_request in response.request.meta["redirect_urls"]:
            if raw_request != response.url:
                return False
        return True

    @staticmethod
    def query_svg_html(svg_info_dict):
        # 获取svg对象
        for key, value in svg_info_dict.items():
            url = value.get("url")
            # 获取svg链接中存储的内容
            response = requests.get(url, headers=header_css_svg, proxies=proxy_dict, timeout=5)
            yield key, response.text

    @staticmethod
    def gain_url(pattern, html):
        """
        获取url
        """
        return "http://{}".format(pattern.search(html).group())

    def search_svg_info(self, css_html):
        """
        从每个 css_html 文件中获取每个类名的前缀，及对应的x轴像素和svg的url
        """
        data_list = self.pattern_svg_info.findall(css_html)
        _dict = dict()
        for (name, x_px, url) in data_list:
            # f"http:{url}" 字符串
            _dict.setdefault(name, {"px": int(x_px), "url": f"http:{url}"})
        return _dict

    def handler_css(self, css_html):
        """
        获取每个加密字对应的坐标值
        """
        data_list = self.pattern_css_info.findall(css_html)
        _dict = dict()
        for (name, x_px, y_px) in data_list:
            _dict.setdefault(name, {"x_px": int(x_px), "y_px": int(y_px)})
        return _dict

    def enter_handler_svg(self, svg_html):
        """
        判断svg页面中是否存在defs字符，用于判断使用哪一种解析规则对数据进行解析
        """
        is_existed_defs = self.pattern_is_existed_defs.search(svg_html)
        if not is_existed_defs:
            return self.handler_svg_v1(svg_html)
        return self.handler_svg_v2(svg_html)

    def handler_svg_v1(self, html):
        """
        应对 <x="",y="">对应文字<> 的数据，此时 x 没有实际用途，只需要关注 y 与 对应文字 即可。
        """
        data_list = self.pattern_handler_svg.findall(html)
        return sorted([(int(y), content.strip()) for y, content in data_list], key=itemgetter(0))

    def handler_svg_v2(self, html):
        """
        与 handler_avg_v1 相比，主要是为了应对 y轴 放在 defs 中， 对应文本放在 textPath 中的情况
        """
        defs_dict = {int(k): int(v) for (v, k) in self.pattern_svg_defs.findall(html)}
        text_path_dict = {int(k): v.strip() for (k, v) in self.pattern_svg_text_path.findall(html)}
        data_list = [(k, text_path_dict.get(v, "")) for k, v in defs_dict.items()]
        return data_list

    def search_info(self, context, css_info_dict, svg_info_dict):
        key_list = self.pattern_tag_info.findall(context)
        for key in key_list:
            value = self.search_word(key, css_info_dict, svg_info_dict)
            context = regex.sub(r'<[a-z]+\s*class="{}"><.*?>'.format(key), value, context)
        return context

    @staticmethod
    def search_word(key, css_info_dict, svg_info_dict):
        """
        查找单词
        """
        def _search(_x_px, _y_px, _data_list):
            for data in _data_list:
                if data[0] > _y_px:
                    return data[1][_x_px]

        px_dict = css_info_dict.get(key, dict())
        x_px, y_px = px_dict.get("x_px", 0), px_dict.get("y_px", 0)

        for k, svg_info in svg_info_dict.items():
            if key.startswith(k):
                width = svg_info.get("px")
                data_list = svg_info.get(k)
                x_px = x_px // width
                return _search(x_px, y_px, data_list)
        return ""

    def handler_text(self, text):
        """
        过滤文本中存在的垃圾
        """
        if not self.pattern_symbol.search(text):
            return text.strip()
        text_list = self.pattern_text.findall(text)
        text_list = [text.strip() for text in text_list if text.strip()]
        text = "".join(text_list)
        return self.pattern_rubbish.sub("", text).strip()

    @staticmethod
    def _hash(raw_str):
        """
        获取字符串 raw_str 对应的hash值
        """
        raw_str = raw_str.encode('utf-8')
        hash_calculator = hashlib.md5()
        hash_calculator.update(raw_str)
        return hash_calculator.hexdigest()

    @staticmethod
    def filter_emoji(desstr, restr='.'):
        """
        过滤表情
        """
        try:
            co = regex.compile('[\U00010000-\U0010ffff]')
        except re.error:
            co = regex.compile('[\uD800-\uDBFF][\uDC00-\uDFFF]')
        return co.sub(restr, desstr)

    def calc_lng_lat(self, address):
        """
        通过地址获取经纬度
        :param address: 店铺地址
        :return {
            lng: 经度
            lat: 纬度
        }
        """
        try:
            url = self.map_url % address
            response = requests.get(url)
            answer = response.json()
            return {
                "lng": answer['result']['location']['lng'],
                "lat": answer['result']['location']['lat']
            }
        except Exception as _:
            return {
                "lng": '',
                "lat": ''
            }

    @staticmethod
    def deal_with_error(error):
        """
        获取详细报错信息
        """
        err_msg = traceback.format_exc()
        err_msg = " ## ".join(err_msg.split("\n"))
        err_msg = " ## ".join([err_msg, str(error)])
        return err_msg

    def __str__(self) -> str:
        """
        命名规范：类名必须只包括 大写字母，小写字母，数字。每个单词的首字母大写
        :return: 把每个单词进行分割，首字母转小写后的字符串。
        """

        def _x(c):
            return 'A' <= c <= 'Z'

        name = self.__class__.__name__
        if DigitLetterPATTERN.search(name):
            raise NameError('类名命令不合法')

        _str = ''
        for index, char in enumerate(name):
            if index == 0 or index + 1 == len(name):
                _str += char
                continue
            if _x(char):
                if not _x(name[index - 1]):
                    _str += '_'
                if _x(name[index - 1]) and (not _x(name[index + 1])):
                    _str += '_'
            _str += char

        return _str.lower()
